package com.crl.rcdp.gateway.test;

import com.cars.util.global.GlobalReturnCode;
import com.cars.util.json.JsonUtil;
import com.cars.util.string.StringUtil;
import com.crl.rcdp.sys.model.SysErrorLog;
import com.crl.rcdp.sys.service.SysLogService;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.Charset;
import java.util.Date;

/**
 * @author: demo
 * @date: 2020年7月3日
 * @Document :  重写响应
 */
@Slf4j
public class RecordHttpResponseDecorator extends ServerHttpResponseDecorator {

    private RecordDO record;
    private SysLogService sysLogService;

    RecordHttpResponseDecorator(ServerHttpResponse delegate, RecordDO record, SysLogService sysLogService) {
        super(delegate);
        this.record = record;
        this.sysLogService = sysLogService;
    }

    /**
     * 基于netty,我这里需要显示的释放一次dataBuffer,但是slice出来的byte是不需要释放的,
     * 与下层共享一个字符串缓冲池,gateway过滤器使用的是nettyWrite类,会发生response数据多次才能返回完全。 在
     * ServerHttpResponseDecorator 之后会释放掉另外一个refCount.
     */
    @Override
    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
        DataBufferFactory bufferFactory = this.bufferFactory();
        if (body instanceof Flux) {
            Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
            Publisher<? extends DataBuffer> re = fluxBody.buffer().map(dataBuffer -> {
                DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                DataBuffer join = dataBufferFactory.join(dataBuffer);//处理DefaultDataBufferFactory 的join方法去合并多个dataBuffers 最大的区别
                // probably should reuse buffers
                byte[] content = new byte[join.readableByteCount()];
                // 数据读入数组
//                dataBuffer.read(content);
                // 释放掉内存
//                DataBufferUtils.release(dataBuffer);
                join.read(content);//核心代码
                DataBufferUtils.release(join);
                // 记录返回值
                String resBody = new String(content, Charset.forName("UTF-8"));
                //如果返回值超过1024，会截取部分，TODO
                record.appendResponseBody(resBody);
                record.setEndTime(new Date());
                record.calcSpendTime();
                try {
                    System.out.println("返回结果：" + record.getResponseBody());
                    JsonNode returnCodeNode = JsonUtil.getJsonNode(record.getResponseBody(), "returnCode");
                    //这里解决一个问题，如果返回的record超过1024字节，不是一个完整的json，所以判断一下不完整的话就不处理
                    if (returnCodeNode != null) {
                        String returnCode = returnCodeNode.textValue();
                        //修改数据,修改返回值和处理时间
                        if (record.getSpendTime().intValue() > 3000) {
                            SysErrorLog sysErrorLog = new SysErrorLog();
                            sysErrorLog.setPath(record.getUrl());
                            sysErrorLog.setClientIp(record.getClientAddress());
                            sysErrorLog.setRequestParam(record.getRequestParam());//请求参数
                            sysErrorLog.setReturnCode(returnCode);
                            sysErrorLog.setTimeConsuming(record.getSpendTime().intValue());
                            sysErrorLog.setResponse(record.getResponseBody());
                            if (sysErrorLog.getResponse().length() > 2000) {
                                sysErrorLog.setResponse(sysErrorLog.getResponse().substring(0, 2000));
                            }
                            sysErrorLog.setErrorType(1);//请求过慢
                            sysErrorLog.setOwner(record.getOwner());
                            sysErrorLog.setAuthType(record.getAuthType());
                            sysLogService.addErrorLog(sysErrorLog);
                        }
                        if (StringUtil.isNullOrEmpty(returnCode) || !returnCode.equals(GlobalReturnCode.OK.getReturnCode())) {
                            //返回结果非00200，超时的记录到异常日志 TODO
                            SysErrorLog sysErrorLog = new SysErrorLog();
                            sysErrorLog.setPath(record.getUrl());
                            sysErrorLog.setClientIp(record.getClientAddress());
                            sysErrorLog.setRequestParam(record.getRequestParam());//请求参数
                            sysErrorLog.setReturnCode(returnCode);
                            sysErrorLog.setTimeConsuming(record.getSpendTime().intValue());
                            sysErrorLog.setResponse(record.getResponseBody());
                            if (sysErrorLog.getResponse().length() > 2000) {
                                sysErrorLog.setResponse(sysErrorLog.getResponse().substring(0, 2000));
                            }
                            sysErrorLog.setErrorType(2);//返回错误码
                            sysErrorLog.setOwner(record.getOwner());
                            sysErrorLog.setAuthType(record.getAuthType());
                            sysLogService.addErrorLog(sysErrorLog);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("Response值修改日志记录出现错误->{}", e);
                }
                byte[] uppedContent = new String(content, Charset.forName("UTF-8")).getBytes();
                return bufferFactory.wrap(uppedContent);
            });
            return super.writeWith(re);
        }
        return super.writeWith(body);
    }

    @Override
    public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
        return writeWith(Flux.from(body).flatMapSequential(p -> p));
    }
}
